/**
 * 
 */
package cn.springsoter.core.security.handler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.common.collect.Maps;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.springsoter.core.security.strategy.impl.ReadSMKeyDefaultStrategy;
import cn.springsoter.core.security.strategy.impl.ReadSMKeyStoreStrategy;

/**
 * 对称加密SM4加/解密字段处理类
 * 
 * mybatis执行顺序：Interceptor -> TypeHandler
 * 
 * @author kevin
 *
 */
@Component
public class SM4EncryptHandler<T> extends BaseTypeHandler<T> {
	//缓存sm4对象
	private static Map<String, SymmetricCrypto> SM4_MAP = Maps.newConcurrentMap();
	private static final String SM4_CACHE_KEY = "SM4_KEY";
	
	@Autowired
	private ReadSMKeyDefaultStrategy readSMKeyDefaultStrategy;
	@Autowired
	private ReadSMKeyStoreStrategy readSMKeyStoreStrategy;
	
	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
		String value = (String)parameter;
		try {
			SymmetricCrypto sm4 = SM4_MAP.get(SM4_CACHE_KEY);
			if(Objects.isNull(sm4)) {
				String key = StringUtils.isBlank(readSMKeyStoreStrategy.readKey()) ? readSMKeyDefaultStrategy.readKey() : readSMKeyStoreStrategy.readKey();
				if(StringUtils.isNotBlank(key)) {
					sm4 = SmUtil.sm4(Base64.decode(key));
					SM4_MAP.put(SM4_CACHE_KEY, sm4);
				}
			}
			
			if(Objects.nonNull(sm4)) {
				value = sm4.encryptBase64(value, CharsetUtil.CHARSET_UTF_8);
			}
		} catch (Exception e) {
		}
		ps.setString(i, value);
	}

	@Override
	public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
		String columnValue = rs.getString(columnName);
		return decryptValue(columnValue);
	}

	@Override
	public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		String columnValue = rs.getString(columnIndex);
		return decryptValue(columnValue);
	}

	@Override
	public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		String columnValue = cs.getString(columnIndex);
		return decryptValue(columnValue);
	}

	/**
	 * 解密
	 * 
	 * @param columnValue
	 * @return
	 */
	private T decryptValue(String columnValue) {
		try {
			SymmetricCrypto sm4 = SM4_MAP.get(SM4_CACHE_KEY);
			if(Objects.isNull(sm4)) {
				String key = StringUtils.isBlank(readSMKeyStoreStrategy.readKey()) ? readSMKeyDefaultStrategy.readKey() : readSMKeyStoreStrategy.readKey();
				if(StringUtils.isNotBlank(key)) {
					sm4 = SmUtil.sm4(Base64.decode(key));
					SM4_MAP.put(SM4_CACHE_KEY, sm4);
				}
			}
			if(Objects.nonNull(sm4)) {
				columnValue = sm4.decryptStr(columnValue, CharsetUtil.CHARSET_UTF_8);
			}
		} catch (Exception e) {
		}
        //有一些可能是空字符
        return (T)columnValue;
	}
}
